package biolabs.util;

import java.util.Collection;

import jsr166y.ForkJoinTask;
import jsr166y.RecursiveAction;
import biolabs.core.BioContext;
import biolabs.core.BioObject;
import biolabs.core.WrappedDelegate;

/**
 * A helper class for concurrent programming for BioLabs.
 *
 * @author Tomas Mikula
 */
public abstract class Concurrency {

	/**
	 * Lets the bio-objects work in parallel.
	 *
	 * @param col bio-objects whose {@link BioObject#proceed(long, long, BioContext)}
	 *   methods will be invoked in parallel.
	 * @param startTime
	 * @param endTime
	 */
	public static <O extends BioObject> void proceed(Collection<O> col, long startTime, long endTime, BioContext ctx) {
		WorkTransformation<O> tr = new WorkTransformation<O>(startTime, endTime, ctx);
		ForkJoinTask.invokeAll(new TransformingCollection<O, RecursiveAction>(col, tr));
	}

	private static final Transformation<WrappedDelegate<?, ?>, RecursiveAction> COMMIT_TR =
			new Transformation<WrappedDelegate<?, ?>, RecursiveAction>() {
		@Override
		public RecursiveAction apply(final WrappedDelegate<?, ?> d) {
			return new RecursiveAction() {
				private static final long serialVersionUID = 1L;
				@Override
				protected void compute() {
					d.commit();
				}
			};
		}
	};

	/**
	 * Invoke {@link WrappedDelegate#commit()}
	 * in parallel on delegates from {@code col}.
	 * @param col delegates whose {@code commit()}
	 *   methods will be invoked in parallel.
	 */
	public static void commit(Collection<? extends WrappedDelegate<?, ?>> col) {
		ForkJoinTask.invokeAll(new TransformingCollection<WrappedDelegate<?, ?>, RecursiveAction>(col, COMMIT_TR));
	}
}


/**
 * Transforms a bio-object into an action that executes its
 * {@link BioObject#proceed(long, long)} method.
 */
class WorkTransformation<O extends BioObject> implements Transformation<O, RecursiveAction> {

	private final long startTime;
	private final long endTime;
	private final BioContext ctx;

	public WorkTransformation(long startTime, long endTime, BioContext ctx){
		this.startTime = startTime;
		this.endTime = endTime;
		this.ctx = ctx;
	}

	@Override
	public RecursiveAction apply(final O obj) {
		return new RecursiveAction(){
			private static final long serialVersionUID = 1L;

			@Override
			protected void compute() {
				obj.proceed(startTime, endTime, ctx);
			}
		};
	}

}